﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using E4RPTools;
using Movements.DataSet;

namespace Movements
{
    public class Database
    {
        #region User

        private static long GetUserId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    Utilizador";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertUser(SqlTransaction sqlTransaction, UtilizadorDataSet.UtilizadorRow user)
        {
            const string query =
                @"  insert 
                    into    Utilizador(Nome, Password, Perfil) 
                    values  (@Nome, @Password, @Perfil)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Nome", user.Nome),
                                        new SqlParameter("Password", user.Password),
                                        new SqlParameter("Perfil", user.Perfil)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                user.Id = GetUserId(sqlDataAccess, sqlTransaction);
            }
        }

        private static void UpdateUser(SqlTransaction sqlTransaction, UtilizadorDataSet.UtilizadorRow user)
        {
            const string query =
                @"  update  Utilizador
                    set     Nome = @Nome,
                            Password = @Password,
                            Perfil = @Perfil
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", user.Id),
                                        new SqlParameter("Nome", user.Nome),
                                        new SqlParameter("Password", user.Password),
                                        new SqlParameter("Perfil", user.Perfil)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }


        public static UtilizadorDataSet GetUsers(SqlTransaction sqlTransaction)
        {
            var dataSet = new UtilizadorDataSet();
            const string query =
                @"  select	Id, Nome, Password, Perfil
                    from	Utilizador
                    order by Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.Utilizador, query);
            return dataSet;
        }

        public static UtilizadorDataSet GetUser(SqlTransaction sqlTransaction, long userId)
        {
            var dataSet = new UtilizadorDataSet();
            const string query =
                @"  select	Id, Nome, Password, Perfil
                    from	Utilizador
                    where   Id = @Id";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.Utilizador, query, new SqlParameter("Id", userId));
            return dataSet;
        }

        public static void SaveUser(SqlTransaction sqlTransaction, UtilizadorDataSet.UtilizadorRow user)
        {
            if (user.RowState == DataRowState.Added)
                InsertUser(sqlTransaction, user);
            else
                UpdateUser(sqlTransaction, user);
        }

        #endregion

        #region Supplier

        private static long GetSupplierId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    Fornecedor";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertSupplier(SqlTransaction sqlTransaction, FornecedorDataSet.FornecedorRow supplier)
        {
            const string query =
                @"  insert 
                    into    Fornecedor(Nome, Contribuinte, Morada, CodigoPostal, Telefone, Telemovel, Email, Contacto) 
                    values  (@Nome, @Contribuinte, @Morada, @CodigoPostal, @Telefone, @Telemovel, @Email, @Contacto)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Nome", supplier.Nome),
                                        new SqlParameter("Contribuinte", supplier.IsContribuinteNull() ? Convert.DBNull : supplier.Contribuinte),
                                        new SqlParameter("Morada", supplier.IsMoradaNull() ? Convert.DBNull : supplier.Morada),
                                        new SqlParameter("CodigoPostal", supplier.IsCodigoPostalNull() ? Convert.DBNull : supplier.CodigoPostal),
                                        new SqlParameter("Telefone", supplier.IsTelefoneNull() ? Convert.DBNull : supplier.Telefone),
                                        new SqlParameter("Telemovel", supplier.IsTelemovelNull() ? Convert.DBNull : supplier.Telemovel),
                                        new SqlParameter("Email", supplier.IsEmailNull() ? Convert.DBNull : supplier.Email),
                                        new SqlParameter("Contacto", supplier.IsContactoNull() ? Convert.DBNull : supplier.Contacto)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                supplier.Id = GetSupplierId(sqlDataAccess, sqlTransaction);
            }
        }

        private static void UpdateSupplier(SqlTransaction sqlTransaction, FornecedorDataSet.FornecedorRow supplier)
        {
            const string query =
                @"  update  Fornecedor
                    set     Nome = @Nome,
                            Contribuinte = @Contribuinte,
                            Morada = @Morada,
                            CodigoPostal = @CodigoPostal,
                            Telefone = @Telefone,
                            Telemovel = @Telemovel,
                            Email = @Email,
                            Contacto = @Contacto
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", supplier.Id),
                                        new SqlParameter("Nome", supplier.Nome),
                                        new SqlParameter("Contribuinte", supplier.IsContribuinteNull() ? Convert.DBNull : supplier.Contribuinte),
                                        new SqlParameter("Morada", supplier.IsMoradaNull() ? Convert.DBNull : supplier.Morada),
                                        new SqlParameter("CodigoPostal", supplier.IsCodigoPostalNull() ? Convert.DBNull : supplier.CodigoPostal),
                                        new SqlParameter("Telefone", supplier.IsTelefoneNull() ? Convert.DBNull : supplier.Telefone),
                                        new SqlParameter("Telemovel", supplier.IsTelemovelNull() ? Convert.DBNull : supplier.Telemovel),
                                        new SqlParameter("Email", supplier.IsEmailNull() ? Convert.DBNull : supplier.Email),
                                        new SqlParameter("Contacto", supplier.IsContactoNull() ? Convert.DBNull : supplier.Contacto)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }


        public static FornecedorDataSet GetSuppliers(SqlTransaction sqlTransaction)
        {
            var dataSet = new FornecedorDataSet();
            const string query =
                @"  select	Id, Nome, Contribuinte, Morada, CodigoPostal, Telefone, Telemovel, Email, Contacto
                    from	Fornecedor
                    order by Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.Fornecedor, query);
            return dataSet;
        }

        public static FornecedorDataSet GetSupplier(SqlTransaction sqlTransaction, long supplierId)
        {
            var dataSet = new FornecedorDataSet();
            const string query =
                @"  select	Id, Nome, Contribuinte, Morada, CodigoPostal, Telefone, Telemovel, Email, Contacto
                    from	Fornecedor
                    where   Id = @Id";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.Fornecedor, query, new SqlParameter("Id", supplierId));
            return dataSet;
        }

        public static void SaveSupplier(SqlTransaction sqlTransaction, FornecedorDataSet.FornecedorRow supplier)
        {
            if (supplier.RowState == DataRowState.Added)
                InsertSupplier(sqlTransaction, supplier);
            else
                UpdateSupplier(sqlTransaction, supplier);
        }

        #endregion

        public static ProdutoFornecedorDataSet GetProductSupplier(SqlTransaction sqlTransaction)
        {
            var dataSet = new ProdutoFornecedorDataSet();
            const string query =
                @"  select	pf.IdProduto, pf.IdFornecedor, f.Nome, pf.ReferenciaFornecedor as Referencia, pf.Valor
                    from	ProdutoFornecedor pf, Fornecedor f
                    where   f.Id = pf.IdFornecedor
                    order by f.Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.ProdutoFornecedor, query);
            return dataSet;
        }

        public static PerfilUtilizadorDataSet GetProfiles()
        {
            var dataSet = new PerfilUtilizadorDataSet();
            dataSet.Perfil.AddPerfilRow((int)UserProfile.Administrator, "Administrador");
            dataSet.Perfil.AddPerfilRow((int)UserProfile.Supervisor, "Supervisor");
            dataSet.Perfil.AddPerfilRow((int)UserProfile.Operator, "Operador");
            return dataSet;
        }

        #region Product

        private static long GetProductId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    Produto";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertProduct(SqlTransaction sqlTransaction, ProdutoDataSet.ProdutoRow product)
        {
            product.Stock = 0;
            const string query =
                @"  insert 
                    into    Produto(Referencia, Designacao, Unidade, Stock, Valor) 
                    values  (@Referencia, @Designacao, @Unidade, @Stock, @Valor)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Referencia", product.Referencia),
                                        new SqlParameter("Designacao", product.Designacao),
                                        new SqlParameter("Unidade", product.IsUnidadeNull() ? Convert.DBNull : product.Unidade),
                                        new SqlParameter("Stock", product.Stock),
                                        new SqlParameter("Valor", product.IsValorNull() ? 0 : product.Valor)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                product.Id = GetProductId(sqlDataAccess, sqlTransaction);
            }
        }

        private static void UpdateProduct(SqlTransaction sqlTransaction, ProdutoDataSet.ProdutoRow product)
        {
            const string query =
                @"  update  Produto
                    set     Referencia = @Referencia,
                            Designacao = @Designacao,
                            Unidade = @Unidade,
                            Stock = @Stock,
                            Valor = @Valor
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", product.Id),
                                        new SqlParameter("Referencia", product.Referencia),
                                        new SqlParameter("Designacao", product.Designacao),
                                        new SqlParameter("Unidade", product.IsUnidadeNull() ? Convert.DBNull : product.Unidade),
                                        new SqlParameter("Stock", product.Stock),
                                        new SqlParameter("Valor", product.IsValorNull() ? 0 : product.Valor)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        private static void InsertProductSupplier(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, ProdutoDataSet.FornecedorRow productSupplier)
        {
            const string query =
                @"  insert 
                    into    ProdutoFornecedor(IdProduto, IdFornecedor, ReferenciaFornecedor, Valor, Stock, DataActualizacao) 
                    values  (@IdProduto, @IdFornecedor, @ReferenciaFornecedor, @Valor, @Stock, @DataActualizacao)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdProduto", productSupplier.IdProduto),
                                        new SqlParameter("IdFornecedor", productSupplier.IdFornecedor),
                                        new SqlParameter("ReferenciaFornecedor", productSupplier.IsReferenciaFornecedorNull() ? Convert.DBNull : productSupplier.ReferenciaFornecedor),
                                        new SqlParameter("Valor", productSupplier.Valor),
                                        new SqlParameter("Stock", productSupplier.Stock),
                                        new SqlParameter("DataActualizacao", productSupplier.DataActualizacao)
                                    };
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());            
        }

        private static void UpdateProductSupplier(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, ProdutoDataSet.FornecedorRow productSupplier)
        {
            const string query =
                @"  update  ProdutoFornecedor
                    set     IdFornecedor = @NovoIdFornecedor,
                            ReferenciaFornecedor = @ReferenciaFornecedor, 
                            Valor = @Valor, 
                            Stock = @Stock, 
                            DataActualizacao = @DataActualizacao
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdProduto", productSupplier.IdProduto),
                                        new SqlParameter("NovoIdFornecedor", productSupplier.IdFornecedor),
                                        new SqlParameter("ReferenciaFornecedor", productSupplier.IsReferenciaFornecedorNull() ? Convert.DBNull : productSupplier.ReferenciaFornecedor),
                                        new SqlParameter("Valor", productSupplier.Valor),
                                        new SqlParameter("Stock", productSupplier.Stock),
                                        new SqlParameter("DataActualizacao", productSupplier.DataActualizacao),
                                        new SqlParameter("IdFornecedor", Convert.ToInt64(productSupplier["IdFornecedor", DataRowVersion.Original]))
                                    };
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        private static void DeleteProductSupplier(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, DataRow productSupplier)
        {
            const string query =
                @"  delete  ProdutoFornecedor
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            var productId = Convert.ToInt64(productSupplier["IdProduto", DataRowVersion.Original]);
            var supplierId = Convert.ToInt64(productSupplier["IdFornecedor", DataRowVersion.Original]);
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("IdProduto", productId), new SqlParameter("IdFornecedor", supplierId));
        }
        

        public static ProdutoDataSet GetProducts(SqlTransaction sqlTransaction)
        {
            var dataSet = new ProdutoDataSet();
            const string produtQuery =
                @"  select	Id, Referencia, Designacao, Unidade, Stock, Valor
                    from	Produto
                    order by Referencia";
            const string supplierQuery =
                @"  select	IdProduto, IdFornecedor, ReferenciaFornecedor, Valor, Stock, DataActualizacao
                    from	ProdutoFornecedor";
            using (var dataAccess = new SqlDataAccess())
            {
                dataAccess.Fill(sqlTransaction, dataSet.Produto, produtQuery);
                dataAccess.Fill(sqlTransaction, dataSet.Fornecedor, supplierQuery);
            }                
            return dataSet;
        }

        public static ProdutoDataSet GetProduct(SqlTransaction sqlTransaction, long productId)
        {
            var dataSet = new ProdutoDataSet();
            const string produtQuery =
                @"  select	Id, Referencia, Designacao, Unidade, Stock, Valor
                    from	Produto
                    where   Id = @Id
                    order by Referencia";
            const string supplierQuery =
                @"  select	IdProduto, IdFornecedor, ReferenciaFornecedor, Valor, Stock, DataActualizacao
                    from	ProdutoFornecedor
                    where   IdProduto = @Id";
            using (var dataAccess = new SqlDataAccess())
            {
                dataAccess.Fill(sqlTransaction, dataSet.Produto, produtQuery, new SqlParameter("Id", productId));
                dataAccess.Fill(sqlTransaction, dataSet.Fornecedor, supplierQuery, new SqlParameter("Id", productId));
            }                
            return dataSet;
        }

        public static void SaveProduct(SqlTransaction sqlTransaction, ProdutoDataSet.ProdutoRow product)
        {
            if (product.RowState == DataRowState.Added)
                InsertProduct(sqlTransaction, product);
            else
                UpdateProduct(sqlTransaction, product);
        }

        public static void UpdateProductSuppliers(SqlTransaction sqlTransaction, ProdutoDataSet.FornecedorDataTable productSuppliers)
        {
            using (var sqlDataAccess = new SqlDataAccess())
            {
                foreach (ProdutoDataSet.FornecedorRow productSupplier in productSuppliers)
                {
                    switch (productSupplier.RowState)
                    {
                        case DataRowState.Added:
                            InsertProductSupplier(sqlDataAccess, sqlTransaction, productSupplier);
                            break;
                        case DataRowState.Modified:
                            UpdateProductSupplier(sqlDataAccess, sqlTransaction, productSupplier);
                            break;
                        case DataRowState.Deleted:
                            DeleteProductSupplier(sqlDataAccess, sqlTransaction, productSupplier);
                            break;
                    }
                }
            }
        }

        public static void UpdateStock(SqlTransaction sqlTransaction, long productId, decimal stock)
        {
            const string query =
                @"  update  Produto
                    set     Stock = @Stock
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", productId),
                                        new SqlParameter("Stock", stock)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        public static decimal GetStock(SqlTransaction sqlTransaction, long productId)
        {
            const string query =
                @"  select	Stock
                    from	Produto
                    where   Id = @Id";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, query, new SqlParameter("Id", productId));
        }

        public static bool ExistsMovements(SqlTransaction sqlTransaction, long productId, long supplierId)
        {
            const string query = 
                @"  select  count(*) 
                    from    ItemMovimento
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            using (var sqlDataAccess = new SqlDataAccess())
                return sqlDataAccess.ExecuteScalar(sqlTransaction, 0, query, new SqlParameter("IdProduto", productId), new SqlParameter("IdFornecedor", supplierId)) > 0;
        }

        public static decimal GetStock(SqlTransaction sqlTransaction, long productId, long supplierId)
        {
            const string query =
                @"  select	Stock
                    from	ProdutoFornecedor
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, query, new SqlParameter("IdProduto", productId), new SqlParameter("IdFornecedor", supplierId));
        }

        public static void UpdateStock(SqlTransaction sqlTransaction, long productId, long supplierId, decimal stock)
        {
            const string query =
                @"  update  ProdutoFornecedor
                    set     Stock = @Stock
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdProduto", productId),
                                        new SqlParameter("IdFornecedor", supplierId),
                                        new SqlParameter("Stock", stock)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        public static void UpdateValue(SqlTransaction sqlTransaction, long productId, long supplierId, decimal value)
        {
            const string query =
                @"  update  ProdutoFornecedor
                    set     Valor = @Valor
                    where   IdProduto = @IdProduto
                            and IdFornecedor = @IdFornecedor";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdProduto", productId),
                                        new SqlParameter("IdFornecedor", supplierId),
                                        new SqlParameter("Valor", value)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        #endregion

        #region Target Entities

        private static long GetTargetEntityId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    EntidadeDestino";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertTargetEntity(SqlTransaction sqlTransaction, EntidadeDestinoDataSet.EntidadeDestinoRow targetEntity)
        {
            const string query =
                @"  insert 
                    into    EntidadeDestino(Nome, Principal) 
                    values  (@Nome, @Principal)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Nome", targetEntity.Nome),
                                        new SqlParameter("Principal", false)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                targetEntity.Id = GetTargetEntityId(sqlDataAccess, sqlTransaction);
            }
        }

        private static void UpdateTargetEntity(SqlTransaction sqlTransaction, EntidadeDestinoDataSet.EntidadeDestinoRow targetEntity)
        {
            const string query =
                @"  update  EntidadeDestino
                    set     Nome = @Nome
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", targetEntity.Id),
                                        new SqlParameter("Nome", targetEntity.Nome)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }


        public static EntidadeDestinoDataSet GetTargetEntities(SqlTransaction sqlTransaction)
        {
            var dataSet = new EntidadeDestinoDataSet();
            const string query =
                @"  select	Id, Nome, Principal
                    from	EntidadeDestino
                    order by Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.EntidadeDestino, query);
            return dataSet;
        }

        public static EntidadeDestinoDataSet GetTargetEntity(SqlTransaction sqlTransaction, long targetEntityId)
        {
            var dataSet = new EntidadeDestinoDataSet();
            const string query =
                @"  select	Id, Nome, Principal
                    from	EntidadeDestino
                    where   Id = @Id
                    order by Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.EntidadeDestino, query, new SqlParameter("Id", targetEntityId));
            return dataSet;
        }

        public static void SaveTargetEntity(SqlTransaction sqlTransaction, EntidadeDestinoDataSet.EntidadeDestinoRow targetEntity)
        {
            if (targetEntity.RowState == DataRowState.Added)
                InsertTargetEntity(sqlTransaction, targetEntity);
            else
                UpdateTargetEntity(sqlTransaction, targetEntity);
        }

        public static void DeleteTargetEntity(SqlTransaction sqlTransaction, long targetEntityId)
        {
            const string query =
                @"  delete  EntidadeDestino
                    where   Id = @Id";
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("Id", targetEntityId));
        }

        public static bool ExistsMovements(SqlTransaction sqlTransaction, long targetEntityId)
        {
            const string query =
                @"  select  count(*) 
                    from    Movimento
                    where   IdEntidadeDestino = @IdEntidadeDestino";
            using (var sqlDataAccess = new SqlDataAccess())
                return sqlDataAccess.ExecuteScalar(sqlTransaction, 0, query, new SqlParameter("IdEntidadeDestino", targetEntityId)) > 0;
        }

        #endregion

        #region Movement Type

        public static TipoMovimentoDataSet GetMovementTypes()
        {
            var dataSet = new TipoMovimentoDataSet();
            dataSet.TipoMovimento.AddTipoMovimentoRow("C", "Entrada");
            dataSet.TipoMovimento.AddTipoMovimentoRow("S", "Saída");
            dataSet.TipoMovimento.AddTipoMovimentoRow("D", "Devolução");
            dataSet.TipoMovimento.AddTipoMovimentoRow("F", "Devolução Fornecedor");
            return dataSet;
        }

        #endregion

        #region Product Unit

        public static UnidadeProdutoDataSet GetProductUnits(SqlTransaction sqlTransaction)
        {
            var dataSet = new UnidadeProdutoDataSet();
            dataSet.UnidadeProduto.AddUnidadeProdutoRow("");
            const string query =
                @"  select	Nome
                    from	UnidadeProduto
                    order by 1";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.UnidadeProduto, query);
            return dataSet;
        }

        #endregion

        #region Movement

        private static long GetMovementId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    Movimento";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertMovement(SqlTransaction sqlTransaction, MovimentoDataSet.MovimentoRow movement)
        {
            const string query =
                @"  insert 
                    into    Movimento(Nome, Data, Tipo, IdEntidadeDestino, IdUtilizador, DataAlteracao, IdUtilizadorAlteracao) 
                    values  (@Nome, @Data, @Tipo, @IdEntidadeDestino, @IdUtilizador, @DataAlteracao, @IdUtilizadorAlteracao)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Nome", movement.Nome),
                                        new SqlParameter("Data", movement.Data),
                                        new SqlParameter("Tipo", movement.Tipo),
                                        new SqlParameter("IdEntidadeDestino", movement.IsIdEntidadeDestinoNull() ? Convert.DBNull : movement.IdEntidadeDestino),
                                        new SqlParameter("IdUtilizador", movement.IdUtilizador),                                        
                                        new SqlParameter("DataAlteracao", movement.DataAlteracao),
                                        new SqlParameter("IdUtilizadorAlteracao", movement.IdUtilizadorAlteracao)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                movement.Id = GetMovementId(sqlDataAccess, sqlTransaction);
            }
        }

        private static void UpdateMovement(SqlTransaction sqlTransaction, MovimentoDataSet.MovimentoRow movement)
        {
            const string query =
                @"  update  Movimento
                    set     Nome = @Nome,
                            Data = @Data, 
                            Tipo = @Tipo, 
                            IdEntidadeDestino = @IdEntidadeDestino, 
                            DataAlteracao = @DataAlteracao, 
                            IdUtilizadorAlteracao = @IdUtilizadorAlteracao
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", movement.Id),
                                        new SqlParameter("Nome", movement.Nome),
                                        new SqlParameter("Data", movement.Data),
                                        new SqlParameter("Tipo", movement.Tipo),
                                        new SqlParameter("IdEntidadeDestino", movement.IsIdEntidadeDestinoNull() ? Convert.DBNull : movement.IdEntidadeDestino),
                                        new SqlParameter("DataAlteracao", movement.DataAlteracao),
                                        new SqlParameter("IdUtilizadorAlteracao", movement.IdUtilizadorAlteracao)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        private static long GetMovementeItemId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(IdItem)
                    from    ItemMovimento";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void InsertMovementItem(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, MovimentoDataSet.ItemMovimentoRow movementItem)
        {
            const string query =
                @"  insert 
                    into    ItemMovimento(Id, IdProduto, IdFornecedor, Quantidade, Observacoes, Valor) 
                    values  (@Id, @IdProduto, @IdFornecedor, @Quantidade, @Observacoes, @Valor)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", movementItem.MovimentoRow.Id),
                                        new SqlParameter("IdProduto", movementItem.IdProduto),
                                        new SqlParameter("IdFornecedor", movementItem.IdFornecedor),
                                        new SqlParameter("Quantidade", movementItem.Quantidade),
                                        new SqlParameter("Observacoes", movementItem.IsObservacoesNull() ? Convert.DBNull : movementItem.Observacoes),
                                        new SqlParameter("Valor", movementItem.Valor)
                                    };
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
            movementItem.IdItem = GetMovementeItemId(sqlDataAccess, sqlTransaction);
        }

        private static void UpdateMovementItem(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, MovimentoDataSet.ItemMovimentoRow movementItem)
        {
            const string query =
                @"  update  ItemMovimento
                    set     IdProduto = @IdProduto,
                            IdFornecedor = @IdFornecedor,
                            Quantidade = @Quantidade,
                            Observacoes = @Observacoes,
                            Valor = @Valor
                    where   IdItem = @IdItem";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdItem", movementItem.IdItem),
                                        new SqlParameter("IdProduto", movementItem.IdProduto),
                                        new SqlParameter("IdFornecedor", movementItem.IdFornecedor),
                                        new SqlParameter("Quantidade", movementItem.Quantidade),
                                        new SqlParameter("Observacoes", movementItem.IsObservacoesNull() ? Convert.DBNull : movementItem.Observacoes),
                                        new SqlParameter("Valor", movementItem.Valor)
                                    };
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        private static void DeleteMovementItem(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction, DataRow movementItem)
        {
            const string query =
                @"  delete  ItemMovimento
                    where   IdItem = @IdItem";
            var requestItemId = Convert.ToInt64(movementItem["IdItem", DataRowVersion.Original]);
            sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("IdItem", requestItemId));
        }

        public static MovimentoDataSet GetMovement(SqlTransaction sqlTransaction, long movementId)
        {
            var dataSet = new MovimentoDataSet();
            const string query =
                @"  select	Id, Nome, Data, Tipo, IdEntidadeDestino, IdUtilizador, DataAlteracao, IdUtilizadorAlteracao
                    from	Movimento
                    where   Id = @Id
                    order by Data";
            const string itemQuery =
                @"  select	Id, IdItem, IdProduto, IdFornecedor, Quantidade, Observacoes, Valor
                    from	ItemMovimento
                    where   Id = @Id
                    order by IdItem";
            using (var dataAccess = new SqlDataAccess())
            {
                dataAccess.Fill(sqlTransaction, dataSet.Movimento, query, new SqlParameter("Id", movementId));
                dataAccess.Fill(sqlTransaction, dataSet.ItemMovimento, itemQuery, new SqlParameter("Id", movementId));
            }                
            return dataSet;
        }

        public static decimal GetMovementQuantity(SqlTransaction sqlTransaction, long movementItemId)
        {
            const string query =
                @"  select	Quantidade
                    from	ItemMovimento
                    where   IdItem = @IdItem";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, query, new SqlParameter("IdItem", movementItemId));            
        }

        public static long GetMovementProduct(SqlTransaction sqlTransaction, long movementItemId)
        {
            const string query =
                @"  select	IdProduto
                    from	ItemMovimento
                    where   IdItem = @IdItem";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar(sqlTransaction, 0, query, new SqlParameter("IdItem", movementItemId));
        }

        public static MovimentosDataSet GetMovements(SqlTransaction sqlTransaction)
        {
            var dataSet = new MovimentosDataSet();
            const string query =
                @"  select	m.Id, i.IdItem, m.Nome, m.Data, m.Tipo, m.IdEntidadeDestino, m.IdUtilizador, m.DataAlteracao, m.IdUtilizadorAlteracao,
                            i.IdProduto, i.IdFornecedor, i.Observacoes,
                            case m.Tipo when 'C' then i.Quantidade when 'D' then i.Quantidade when 'F' then -1*i.Quantidade when 'S' then -1*i.Quantidade end as QuantidadeEntrada, 
                            case m.Tipo when 'C' then 0 when 'D' then -1*i.Quantidade when 'F' then 0 when 'S' then i.Quantidade end as QuantidadeSaida,
                            i.Valor
                    from	Movimento m, ItemMovimento i
                    where   i.Id = m.Id
                    order by m.Data";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.Movimento, query);
            return dataSet;
        }

        public static void SaveMovement(SqlTransaction sqlTransaction, MovimentoDataSet.MovimentoRow movement)
        {
            if (movement.RowState == DataRowState.Added)
                InsertMovement(sqlTransaction, movement);
            else
                UpdateMovement(sqlTransaction, movement);
        }

        public static void UpdateMovementItems(SqlTransaction sqlTransaction, MovimentoDataSet.ItemMovimentoDataTable movementItems)
        {
            using (var sqlDataAccess = new SqlDataAccess())
            {
                foreach (MovimentoDataSet.ItemMovimentoRow movementItem in movementItems)
                {
                    switch (movementItem.RowState)
                    {
                        case DataRowState.Added:
                            InsertMovementItem(sqlDataAccess, sqlTransaction, movementItem);
                            break;
                        case DataRowState.Modified:
                            UpdateMovementItem(sqlDataAccess, sqlTransaction, movementItem);
                            break;
                        case DataRowState.Deleted:
                            DeleteMovementItem(sqlDataAccess, sqlTransaction, movementItem);
                            break;
                    }
                }
            }
        }

        public static ImpressaoMovimentoDataSet GetPrintMovement(SqlTransaction sqlTransaction, long movementId)
        {
            var dataSet = new ImpressaoMovimentoDataSet();
            const string query =
                @"  select	mov.Id, mov.Nome, mov.Data, 
                            case mov.Tipo when 'C' then 'Entrada' when 'D' then 'Devolução' when 'S' then 'Saída' when 'F' then 'Devolução Fornecedor' end as Tipo,
                            ed.Nome as EntidadeDestino, uti.Nome as Utilizador, mov.DataAlteracao, uta.Nome as UtilizadorAlteracao
                    from	Movimento mov LEFT OUTER JOIN EntidadeDestino ed ON (ed.Id = mov.IdEntidadeDestino), Utilizador uti, Utilizador uta
                    where   mov.Id = @Id
                            and uti.Id = mov.IdUtilizador
                            and uta.Id = mov.IdUtilizadorAlteracao";
            const string itemQuery =
                @"  select	im.Id, im.IdItem, 
                            p.Referencia as ReferenciaProduto, p.Designacao as DesignacaoProduto, 
                            f.Nome as NomeFornecedor, 
                            pf.ReferenciaFornecedor as ReferenciaFornecedor,
                            im.Quantidade, im.Observacoes, im.Valor
                    from	ItemMovimento im, Produto p, Fornecedor f, ProdutoFornecedor pf
                    where   im.Id = @Id
                            and p.Id = im.IdProduto
                            and f.Id = im.IdFornecedor
                            and pf.IdProduto = im.IdProduto
							and pf.IdFornecedor = im.IdFornecedor
                    order by im.IdItem";
            using (var dataAccess = new SqlDataAccess())
            {
                dataAccess.Fill(sqlTransaction, dataSet.Movimento, query, new SqlParameter("Id", movementId));
                dataAccess.Fill(sqlTransaction, dataSet.ItemMovimento, itemQuery, new SqlParameter("Id", movementId));
            }                
            return dataSet;
        }

        public static bool ExistsProductMovements(SqlTransaction sqlTransaction, long targetEntityId, long productId)
        {
            const string query =
                @"  select	count(*)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar(sqlTransaction, 0, query, new SqlParameter("IdEntidadeDestino", targetEntityId), new SqlParameter("IdProduto", productId)) > 0;            
        }

        public static decimal GetTargetEntityProductStock(SqlTransaction sqlTransaction, long targetEntityId, long productId, long movementItemId)
        {
            const string exitQuery =
                @"  select	sum(i.quantidade)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and m.Tipo = 'S'
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto";
            const string supplierReturnQuery =
                @"  select	sum(i.quantidade)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and m.Tipo = 'F'
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto
		                    and i.IdItem != @IdItem";
            using (var dataAccess = new SqlDataAccess())
            {
                var exitValue = dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, exitQuery, 
                    new SqlParameter("IdEntidadeDestino", targetEntityId), new SqlParameter("IdProduto", productId));
                var supplierReturnValue = dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, supplierReturnQuery,
                    new SqlParameter("IdEntidadeDestino", targetEntityId), new SqlParameter("IdProduto", productId), new SqlParameter("IdItem", movementItemId));
                return exitValue - supplierReturnValue;
            }            
        }

        public static bool ExistsProductMovements(SqlTransaction sqlTransaction, long targetEntityId, long productId, long supplierId)
        {
            const string query =
                @"  select	count(*)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto
                            and i.IdFornecedor = @IdFornecedor";
            using (var dataAccess = new SqlDataAccess())
                return dataAccess.ExecuteScalar(sqlTransaction, 0, query,
                                             new SqlParameter("IdEntidadeDestino", targetEntityId),
                                             new SqlParameter("IdProduto", productId),
                                             new SqlParameter("IdFornecedor", supplierId)) > 0;
        }

        public static decimal GetTargetEntityProductStock(SqlTransaction sqlTransaction, long targetEntityId, long productId, long supplierId, long movementItemId)
        {
            const string exitQuery =
                @"  select	sum(i.quantidade)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and m.Tipo = 'S'
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto
                            and i.IdFornecedor = @IdFornecedor";
            const string supplierReturnQuery =
                @"  select	sum(i.quantidade)
                    from	movimento m, itemmovimento i
                    where	m.IdEntidadeDestino = @IdEntidadeDestino
		                    and m.Tipo = 'F'
		                    and i.Id = m.Id
		                    and i.IdProduto = @IdProduto
		                    and i.IdItem != @IdItem
                            and i.IdFornecedor = @IdFornecedor";
            using (var dataAccess = new SqlDataAccess())
            {
                var exitValue = dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, exitQuery,
                    new SqlParameter("IdEntidadeDestino", targetEntityId), new SqlParameter("IdProduto", productId), new SqlParameter("IdFornecedor", supplierId));
                var supplierReturnValue = dataAccess.ExecuteScalar<decimal>(sqlTransaction, 0, supplierReturnQuery,
                    new SqlParameter("IdEntidadeDestino", targetEntityId), new SqlParameter("IdProduto", productId), new SqlParameter("IdFornecedor", supplierId),
                    new SqlParameter("IdItem", movementItemId));
                return exitValue - supplierReturnValue;
            }
        }

        #endregion

        #region Layout

        private static bool ExistsLayout(SqlTransaction sqlTransaction, string lista, string nome)
        {
            int count;
            const string query =
                @"  select	count(*)
		            from	ListaLayout
                    where   Lista = @Lista
                            and Nome = @Nome";
            using (var dataAccess = new SqlDataAccess())
                count = dataAccess.ExecuteScalar(sqlTransaction, 0, query, new SqlParameter("Lista", lista),
                    new SqlParameter("Nome", nome));
            return count > 0;
        }

        private static void InsertLayout(SqlTransaction sqlTransaction, string list, string name, string xmlLayout)
        {
            const string query =
                @"  insert 
                    into    ListaLayout(Lista, Nome, XmlLayout) 
                    values  (@List, @Name, @XmlLayout)";
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("List", list),
                    new SqlParameter("Name", name), new SqlParameter("XmlLayout", xmlLayout));
        }

        private static void UpdateLayout(SqlTransaction sqlTransaction, string lista, string nome, string xmlLayout)
        {
            const string query =
                @"  update  ListaLayout 
                    set     XmlLayout = @XmlLayout
                    where   Lista = @Lista
                            and Nome = @Nome";
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("Lista", lista),
                    new SqlParameter("Nome", nome), new SqlParameter("XmlLayout", xmlLayout));
        }


        public static ListaLayoutDataSet GetListLayouts(SqlTransaction sqlTransaction, string lista)
        {
            var dataSet = new ListaLayoutDataSet();
            const string query =
                @"  select	Lista, Nome, XmlLayout
		            from	ListaLayout
                    where   Lista = @Lista
                    order by Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.ListaLayout, query, new SqlParameter("Lista", lista));
            return dataSet;
        }

        public static void SaveLayout(SqlTransaction sqlTransaction, string lista, string nome, string xmlLayout)
        {
            if (ExistsLayout(sqlTransaction, lista, nome))
                UpdateLayout(sqlTransaction, lista, nome, xmlLayout);
            else
                InsertLayout(sqlTransaction, lista, nome, xmlLayout);

        }

        #endregion

        #region Product Tree

        private static long GetProductTreeId(SqlDataAccess sqlDataAccess, SqlTransaction sqlTransaction)
        {
            const string query =
                @"  select  max(Id)
                    from    ArvoreProduto";
            return sqlDataAccess.ExecuteScalar(sqlTransaction, -1, query);
        }

        private static void AddProductTree(SqlTransaction sqlTransaction, ArvoreProdutoDataSet.ArvoreProdutoRow productTree)
        {
            const string query =
                @"  insert 
                    into    ArvoreProduto(IdPai, Nome, Tipo, IdProduto) 
                    values  (@IdPai, @Nome, @Tipo, @IdProduto)";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdPai", productTree.IsIdPaiNull() ? Convert.DBNull : productTree.IdPai),
                                        new SqlParameter("Nome", productTree.Nome),
                                        new SqlParameter("Tipo", productTree.Tipo),
                                        new SqlParameter("IdProduto", productTree.IsIdProdutoNull() ? Convert.DBNull : productTree.IdProduto)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
            {
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
                productTree.Id = GetProductTreeId(sqlDataAccess, sqlTransaction);
            }
        }


        public static ArvoreProdutoDataSet GetProductTree(SqlTransaction sqlTransaction)
        {
            var dataSet = new ArvoreProdutoDataSet();
            const string query =
                @"  select	Id, IdPai, Nome, Tipo, IdProduto
		            from	ArvoreProduto
                    order by Tipo, Nome";
            using (var dataAccess = new SqlDataAccess())
                dataAccess.Fill(sqlTransaction, dataSet.ArvoreProduto, query);
            return dataSet;
        }

        public static void UpdateProductTree(SqlTransaction sqlTransaction, long id, string name)
        {
            const string query =
                @"  update  ArvoreProduto 
                    set     Nome = @Nome
                    where   Id = @Id";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("Id", id),
                                        new SqlParameter("Nome", name)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        public static void SaveProductTree(SqlTransaction sqlTransaction, ArvoreProdutoDataSet dataSet)
        {
            foreach (ArvoreProdutoDataSet.ArvoreProdutoRow productTree in dataSet.ArvoreProduto)
                AddProductTree(sqlTransaction, productTree);
        }

        public static void DeleteProductTree(SqlTransaction sqlTransaction, long productTreeId)
        {
            const string query =
                @"  delete  ArvoreProduto
                    where   Id = @Id";
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, new SqlParameter("Id", productTreeId));
        }

        public static void UpdateProductTreeProduct(SqlTransaction sqlTransaction, long productId, string name)
        {
            const string query =
                @"  update  ArvoreProduto 
                    set     Nome = @Nome
                    where   IdProduto = @IdProduto";
            var sqlParameters = new List<SqlParameter>
                                    {
                                        new SqlParameter("IdProduto", productId),
                                        new SqlParameter("Nome", name)
                                    };
            using (var sqlDataAccess = new SqlDataAccess())
                sqlDataAccess.ExecuteNonQuery(sqlTransaction, query, sqlParameters.ToArray());
        }

        #endregion
    }
}
